import { SimJobStatus } from '../type';
import { SimulationJob } from '../entities/simulationJob';
import { EntityRepository, Repository, In, UpdateResult } from 'typeorm';
import { AgentSimulationJobCount, SimulationJobPageQuery } from '../dtos';
import buildPaginator from 'pagination-apis';
import { Agent } from '../../agent/entities/agent';
export interface ISimulationJobRepository {
  findByStatus(status: SimJobStatus): Promise<SimulationJob[]>;
  findByContext(userId: string, appId: string, nodeId: string): Promise<SimulationJob[]>;
  findByProductAndStatus(productName: string, status: SimJobStatus): Promise<SimulationJob[]>;
  findByContextAndBusinessId(userId: string, appId: string, nodeId: string, businessId: string): Promise<SimulationJob | undefined>;
  findByContextAndBusinessIds(userId: string, appId: string, nodeId: string, businessId: string[]): Promise<SimulationJob[]>;
  deleteByContextAndBusinessId(userId: string, appId: string, nodeId: string, businessId: string): Promise<void>;
  findForPage(simulationJobPageQuery: SimulationJobPageQuery): Promise<any>;
  countAgentSimulationJobStatus(agents: string[], statuses: string[], product: string): Promise<AgentSimulationJobCount[]>;
  countJobByAgentAndStatuses(agents: string[], statuses: string[], product: string): Promise<AgentSimulationJobCount[]>;
  findTimeoutJobs(timeout: number): Promise<SimulationJob[]>;
  findByNodeIdAndStatuses(nodeId: string, statuses: string[]): Promise<SimulationJob[]>;
  getLatestByBusinessIdAndProduct(businessId: string, product: string): Promise<SimulationJob>;
}

@EntityRepository(SimulationJob)
export class SimulationJobRepository extends Repository<SimulationJob> implements ISimulationJobRepository {

  async getLatestByBusinessIdAndProduct(businessId: string, product: string): Promise<SimulationJob> {
    return await this.findOne({
      where: { business_id: businessId, product },
      order: { id: 'DESC' }
    });
  }

  async findByStatus(status: SimJobStatus): Promise<SimulationJob[]> {
    return await this.find({ where: { status } });
  }

  async findByProductAndStatus(productName: string, status: SimJobStatus): Promise<SimulationJob[]> {
    return await this.find({ where: { product: productName, status } });
  }

  async findByContext(userId: string, appId: string, nodeId: string): Promise<SimulationJob[]> {
    return await this.find({ where: { user_id: userId, app_id: appId, node_id: nodeId } });
  }

  async findByContextAndBusinessId(userId: string, appId: string, nodeId: string, businessId: string) {
    return await this.findOne({ where: { user_id: userId, app_id: appId, node_id: nodeId, business_id: businessId } });
  }

  /**
   * @link https://github.com/typeorm/typeorm/issues/1239#issuecomment-431796732
   */
  async findByContextAndBusinessIds(userId: string, appId: string, nodeId: string, businessIds: string[]): Promise<SimulationJob[]> {
    return await this.find({
      where: { user_id: userId, app_id: appId, node_id: nodeId, business_id: In(businessIds) }
    });
  }
  async deleteByContextAndBusinessId(userId: string, appId: string, nodeId: string, businessId: string) {
    await this.delete({ user_id: userId, app_id: appId, node_id: nodeId, business_id: businessId });
  }

  async findForPage(simulationJobPageQuery: SimulationJobPageQuery): Promise<any> {
    const { skip, limit, paginate } = buildPaginator({ page: simulationJobPageQuery.currentPage || 1, limit: simulationJobPageQuery.pageSize || 10 });
    let query = this.createQueryBuilder('simulationJobPage')
      .where('user_id = :userId and app_id = :appId and node_id = :nodeId',
        { userId: simulationJobPageQuery.userId, appId: simulationJobPageQuery.appId, nodeId: simulationJobPageQuery.nodeId });

    if (simulationJobPageQuery.businessId) {
      query = query.andWhere('business_id like :businessId', { businessId: '%' + simulationJobPageQuery.businessId + '%' });
    }
    if (simulationJobPageQuery.statuses) {
      query = query.andWhere('status in (:...statuses)', { statuses: simulationJobPageQuery.statuses });
    }

    // 时间范围查询 - 使用 submit_at 字段，因为 started_at 可能为 null
    if (simulationJobPageQuery.startTime && simulationJobPageQuery.endTime) {
      query = query.andWhere('submit_at >= :startTime AND submit_at <= :endTime', {
        startTime: new Date(simulationJobPageQuery.startTime),
        endTime: new Date(simulationJobPageQuery.endTime)
      });
    } else if (simulationJobPageQuery.startTime) {
      query = query.andWhere('submit_at >= :startTime', {
        startTime: new Date(simulationJobPageQuery.startTime)
      });
    } else if (simulationJobPageQuery.endTime) {
      query = query.andWhere('submit_at <= :endTime', {
        endTime: new Date(simulationJobPageQuery.endTime)
      });
    }

    const [data, total] = await query.addOrderBy('id', 'DESC')
      .skip(skip)
      .take(limit)
      .getManyAndCount();
    return paginate(data, total);
  }

  async countAgentSimulationJobStatus(agents: string[], statuses: string[], product: string): Promise<AgentSimulationJobCount[]> {
    return await this.createQueryBuilder()
      .select(['agent_id as identity', 'count(*) as count', 'status'])
      .where('product = :product and agent_id IN (:...agentIds) and status In (:...statuses)', { agentIds: agents, statuses: statuses, product: product })
      .groupBy('agent_id')
      .addGroupBy('status')
      .getRawMany();
  }

  async countJobByAgentAndStatuses(agents: string[], statuses: string[], product: string): Promise<AgentSimulationJobCount[]> {
    return await this.createQueryBuilder()
      .select(['agent_id as identity', 'count(*) as count'])
      .where('product = :product and agent_id IN (:...agentIds) and status In (:...statuses)', { agentIds: agents, statuses: statuses, product: product })
      .groupBy('agent_id')
      .getRawMany();
  }

  async findTimeoutJobs(timeout: number) {
    return await this.createQueryBuilder()
      .where(`(status = 'running' and started_at < now() - interval '${timeout} hour')`)
      .orWhere(`(status = 'agentDispatched' and created_at < now() - interval '${timeout} hour')`)
      .getMany();
  }

  async findByNodeIdAndStatuses(nodeId: string, statuses: string[]): Promise<SimulationJob[]> {
    return await this.find({
      where: { node_id: nodeId, status: In(statuses) }
    });
  }

}